package demo.generate.Demo_SetField;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import demo.generate.MyClassLoader;
import jdk.internal.org.objectweb.asm.Type;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Created by zhangyanfei on 2018/10/19.
 *
 * @author zhangyanfei
 * @date 2018/10/19
 */
public class GenerateClassV2 {

    public static Object generateClass()
        throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
        InstantiationException {
        String className = "demo/generate/Demo_SetField/AClass";
        ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, className, null, Type.getInternalName(Object.class), null);

        FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "name", Type.getDescriptor(String.class), null, null);
        fv.visitEnd();

        {
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V");
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitLdcInsn("Tom");
            mv.visitFieldInsn(Opcodes.PUTFIELD, className, "name", Type.getDescriptor(String.class));
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "setName",
                "(" + Type.getDescriptor(String.class) + ")V", null, null);
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitVarInsn(Opcodes.ALOAD, 1);
            mv.visitFieldInsn(Opcodes.PUTFIELD, className, "name", Type.getDescriptor(String.class));
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getName", "()" + Type.getDescriptor(String.class),
                null, null);
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitFieldInsn(Opcodes.GETFIELD, className, "name", Type.getDescriptor(String.class));
            mv.visitInsn(Opcodes.ARETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }

        byte[] code = cw.toByteArray();
        FileOutputStream fos = new FileOutputStream(
            "/Users/zhangyanfei/workspace/hissy-common/src/test/java/demo/generate/Demo_SetField/AClass.class");
        fos.write(code);
        fos.close();

        Class<?> aClass = new MyClassLoader().defineClass("demo.generate.Demo_SetField.AClass", cw.toByteArray());

        return aClass.getConstructor().newInstance();
    }
}
